#include "SelectDispatcher.h"

// 实例化SelectDispatcher
SelectDispatcher::SelectDispatcher(EventLoop* evLoop) : Dispatcher(evLoop) {
	FD_ZERO(&_readSet);
	FD_ZERO(&_writeSet);
	_name = "select";
}

// 销毁SelectDispatcher
SelectDispatcher::~SelectDispatcher() {}

// 将[_curChannel.fd]添加到select的事件检测集合
void SelectDispatcher::setFd() {
	if (_curChannel->getEvents() & static_cast<int>(FdEvent::_ReadEvent)) {
		FD_SET(_curChannel->getFd(), &_readSet);
	}
	if (_curChannel->getEvents() & static_cast<int>(FdEvent::_WriteEvent)) {
		FD_SET(_curChannel->getFd(), &_writeSet);
	}
}

// 将[_curChannel.fd]从select的事件检测集合中删除
void SelectDispatcher::deleteFd() {
	if (_curChannel->getEvents() & static_cast<int>(FdEvent::_ReadEvent)) {
		FD_CLR(_curChannel->getFd(), &_readSet);
	}
	if (_curChannel->getEvents() & static_cast<int>(FdEvent::_WriteEvent)) {
		FD_CLR(_curChannel->getFd(), &_writeSet);
	}
}

// 将[_curChannel.fd]添加到select模型
int SelectDispatcher::add() {
	if (_curChannel->getFd() >= _Max) { return -1; }
	setFd();
	return 0;
}

// 将[_curChannel.fd]从select模型中删除, 并释放_curChannel所属的TcpConnection
int SelectDispatcher::remove() {
	if (_curChannel->getFd() >= _Max) { return -1; }
	deleteFd();

	// 通过channel释放对应的tcpConnection资源
	_curChannel->_destroyCallback(const_cast<void*>(_curChannel->getArg()));
	return 0;
}

// 修改select模型中的[_curChannel.fd]的属性
int SelectDispatcher::modify() {
	if (_curChannel->getEvents() & static_cast<int>(FdEvent::_ReadEvent)) {
		FD_SET(_curChannel->getFd(), &_readSet);
		FD_CLR(_curChannel->getFd(), &_writeSet);
	}
	if (_curChannel->getEvents() & static_cast<int>(FdEvent::_WriteEvent)) {
		FD_SET(_curChannel->getFd(), &_writeSet);
		FD_CLR(_curChannel->getFd(), &_readSet);
	}

	// 一般服务器等待客户端请求时监视读事件，在准备给客户端发送响应时监视写事件，对同一fd不会同时监视读写事件
	// 但写事件一般都是就绪的，所以要发送数据时，改为监视写事件，则写事件的回调函数会被立刻调用，在发送完数据后
	// 改为监视读事件即可
	return 0;
}

// 检测select模型上的[_curChannel.fd]是否被激活(是否有就绪事件), 阻塞等待timeout秒, 并处理就绪事件
int SelectDispatcher::dispatch(int timeout) {
	// 1. 等待事件就绪
	struct timeval val;
	val.tv_sec = timeout;
	val.tv_usec = 0;
	fd_set readTmp = _readSet;
	fd_set writeTmp = _writeSet;
	int count = select(_Max, &readTmp, &writeTmp, NULL, &val);
	if (count == -1) {
		perror("select");
		return -1;
	}

	// 2. 处理就绪事件
	for (int i = 0; i < _Max; ++i) {
		if (FD_ISSET(i, &readTmp)) { _eventLoop->eventActive(i, static_cast<int>(FdEvent::_ReadEvent)); }
		if (FD_ISSET(i, &writeTmp)) { _eventLoop->eventActive(i, static_cast<int>(FdEvent::_WriteEvent)); }
	}
	return 0;
}